UNPKG

@defikitdotnet/education-module-ai

Version:
198 lines (197 loc) 16.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var jsx_runtime_1 = require("react/jsx-runtime"); var react_1 = require("react"); var router_1 = require("next/router"); var axios_1 = __importDefault(require("axios")); var sonner_1 = require("sonner"); // UI Components var button_1 = require("../../../components/ui/button"); var card_1 = require("../../../components/ui/card"); var accordion_1 = require("../../../components/ui/accordion"); var separator_1 = require("../../../components/ui/separator"); var badge_1 = require("../../../components/ui/badge"); var skeleton_1 = require("../../../components/ui/skeleton"); // Define level name mapping for UI display var levelNames = { beginner: "Beginner", intermediate: "Intermediate", advanced: "Advanced", }; var CourseDetailPage = function () { var router = (0, router_1.useRouter)(); var courseId = router.query.id; var _a = (0, react_1.useState)(null), courseData = _a[0], setCourseData = _a[1]; var _b = (0, react_1.useState)(true), loading = _b[0], setLoading = _b[1]; var _c = (0, react_1.useState)(null), error = _c[0], setError = _c[1]; var _d = (0, react_1.useState)(false), isEnrolling = _d[0], setIsEnrolling = _d[1]; var _e = (0, react_1.useState)(null), enrollment = _e[0], setEnrollment = _e[1]; var apiBaseUrl = process.env.NEXT_PUBLIC_BACKEND_URL || ""; var teacherId = router.query.teacherId || (courseData === null || courseData === void 0 ? void 0 : courseData.teacherId) || ""; // Fetch course details (0, react_1.useEffect)(function () { var fetchCourseData = function () { return __awaiter(void 0, void 0, void 0, function () { var response, enrollmentResponse, enrollError_1, err_1; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!courseId || typeof courseId !== "string") { setError("Invalid Course ID"); setLoading(false); return [2 /*return*/]; } _c.label = 1; case 1: _c.trys.push([1, 7, 8, 9]); setLoading(true); setError(null); return [4 /*yield*/, axios_1.default.get("".concat(apiBaseUrl, "/api/courses/").concat(courseId))]; case 2: response = _c.sent(); setCourseData(response.data); _c.label = 3; case 3: _c.trys.push([3, 5, , 6]); return [4 /*yield*/, axios_1.default.get("".concat(apiBaseUrl, "/api/enrollments/student/course/").concat(courseId), { withCredentials: true })]; case 4: enrollmentResponse = _c.sent(); if (enrollmentResponse.data) { setEnrollment(enrollmentResponse.data); } return [3 /*break*/, 6]; case 5: enrollError_1 = _c.sent(); // Not enrolled yet, which is fine console.log("Student not enrolled yet"); return [3 /*break*/, 6]; case 6: return [3 /*break*/, 9]; case 7: err_1 = _c.sent(); console.error("Error fetching course data:", err_1); setError(((_b = (_a = err_1.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || "Failed to load course data. Please try again."); return [3 /*break*/, 9]; case 8: setLoading(false); return [7 /*endfinally*/]; case 9: return [2 /*return*/]; } }); }); }; if (router.isReady) { fetchCourseData(); } }, [router.isReady, courseId, apiBaseUrl]); // Handle enrollment var handleEnrollment = function () { return __awaiter(void 0, void 0, void 0, function () { var response, errorData, enrollmentData, err_2; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!courseId) return [2 /*return*/]; setIsEnrolling(true); _a.label = 1; case 1: _a.trys.push([1, 6, 7, 8]); console.log("[ENROLLMENT DEBUG] Starting enrollment for course ".concat(courseId)); return [4 /*yield*/, fetch("".concat(apiBaseUrl, "/api/enrollments"), { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ courseId: courseId }), credentials: 'include', // Crucial for sending cookies mode: 'cors' })]; case 2: response = _a.sent(); console.log("[ENROLLMENT DEBUG] Response status:", response.status); if (!!response.ok) return [3 /*break*/, 4]; return [4 /*yield*/, response.json()]; case 3: errorData = _a.sent(); console.error("[ENROLLMENT DEBUG] Error response:", errorData); throw new Error(errorData.error || 'Failed to enroll in course'); case 4: return [4 /*yield*/, response.json()]; case 5: enrollmentData = _a.sent(); console.log("[ENROLLMENT DEBUG] Enrollment successful:", enrollmentData); setEnrollment(enrollmentData); sonner_1.toast.success("Successfully enrolled in course!"); // Log before redirect console.log("[ENROLLMENT DEBUG] Redirecting to: /student/content/".concat(courseId, "?teacherId=").concat(teacherId || '')); // Redirect to the content page with teacherId parameter router.push("/student/content/".concat(courseId, "?teacherId=").concat(teacherId || '')); return [3 /*break*/, 8]; case 6: err_2 = _a.sent(); console.error("[ENROLLMENT DEBUG] Error enrolling in course:", err_2); sonner_1.toast.error(err_2.message || "Failed to enroll in course. Please try again."); return [3 /*break*/, 8]; case 7: setIsEnrolling(false); return [7 /*endfinally*/]; case 8: return [2 /*return*/]; } }); }); }; // Continue learning var handleContinueLearning = function () { // Log before redirect console.log("[CONTINUE DEBUG] Redirecting to: /student/content/".concat(courseId, "?teacherId=").concat(teacherId || '')); router.push("/student/content/".concat(courseId, "?teacherId=").concat(teacherId || '')); }; // Return to dashboard var handleBackToDashboard = function () { router.push("/student?teacherId=".concat(teacherId)); }; if (loading) { return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-4xl mx-auto p-6", children: [(0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-12 w-3/4 mb-4" }), (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-40 w-full mb-8" }), (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-8 w-1/2 mb-4" }), (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-24 w-full mb-4" }), (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-24 w-full mb-4" }), (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-24 w-full mb-4" })] })); } if (error || !courseData) { return ((0, jsx_runtime_1.jsx)("div", { className: "max-w-4xl mx-auto p-6", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { className: "bg-red-50 dark:bg-red-900/20 border-red-300 dark:border-red-700", children: [(0, jsx_runtime_1.jsx)(card_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-red-700 dark:text-red-400", children: "Error Loading Course" }) }), (0, jsx_runtime_1.jsx)(card_1.CardContent, { children: (0, jsx_runtime_1.jsx)("p", { className: "text-red-600 dark:text-red-400", children: error || "Failed to load course details" }) }), (0, jsx_runtime_1.jsx)(card_1.CardFooter, { children: (0, jsx_runtime_1.jsx)(button_1.Button, { onClick: handleBackToDashboard, variant: "outline", children: "Back to Dashboard" }) })] }) })); } return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-4xl mx-auto p-6", children: [(0, jsx_runtime_1.jsx)(sonner_1.Toaster, { position: "top-center", richColors: true }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", onClick: handleBackToDashboard, className: "mb-6", children: "\u2190 Back to Dashboard" }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-3xl font-bold", children: courseData.title }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center mt-2 space-x-4", children: [(0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "secondary", children: levelNames[courseData.level] || courseData.level }), courseData.categoryNames && courseData.categoryNames.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "flex gap-2", children: courseData.categoryNames.map(function (category, idx) { return ((0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "outline", children: category }, idx)); }) }))] }), courseData.thumbnail && ((0, jsx_runtime_1.jsx)("div", { className: "mt-4 rounded-lg overflow-hidden aspect-video", children: (0, jsx_runtime_1.jsx)("img", { src: courseData.thumbnail, alt: "".concat(courseData.title, " thumbnail"), className: "w-full h-full object-cover" }) })), (0, jsx_runtime_1.jsx)("div", { className: "mt-4 prose dark:prose-invert max-w-none", children: (0, jsx_runtime_1.jsx)("p", { children: courseData.description }) })] }), (0, jsx_runtime_1.jsx)(separator_1.Separator, { className: "my-6" }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-bold mb-4", children: "Course Content" }), (0, jsx_runtime_1.jsx)(accordion_1.Accordion, { type: "single", collapsible: true, className: "w-full", children: courseData.sections.map(function (section) { return ((0, jsx_runtime_1.jsxs)(accordion_1.AccordionItem, { value: section.id, children: [(0, jsx_runtime_1.jsx)(accordion_1.AccordionTrigger, { children: section.title }), (0, jsx_runtime_1.jsx)(accordion_1.AccordionContent, { children: (0, jsx_runtime_1.jsxs)("div", { className: "pl-4 space-y-3", children: [section.lectures.map(function (lecture) { return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center py-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium", children: lecture.title }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: [lecture.type === "text" && "Text Lecture", lecture.type === "video" && "Video Lecture", lecture.type === "h5p" && "Interactive Content"] })] }), (0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "outline", children: lecture.type })] }, lecture.id)); }), section.quiz && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center py-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium", children: section.quiz.title }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: ["Quiz \u2022 ", section.quiz.questionsCount || "Multiple", " questions"] })] }), (0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "outline", className: "bg-blue-100 dark:bg-blue-900/30", children: "Quiz" })] }))] }) })] }, section.id)); }) })] }), (0, jsx_runtime_1.jsxs)(card_1.Card, { className: "mt-6 shadow-md", children: [(0, jsx_runtime_1.jsxs)(card_1.CardHeader, { children: [(0, jsx_runtime_1.jsx)(card_1.CardTitle, { children: "Ready to begin your learning journey?" }), (0, jsx_runtime_1.jsx)(card_1.CardDescription, { children: enrollment ? ("You've already enrolled in this course. Continue your learning journey!") : ("Enroll in this course to track your progress and access all learning materials.") })] }), (0, jsx_runtime_1.jsx)(card_1.CardFooter, { children: enrollment ? ((0, jsx_runtime_1.jsxs)(button_1.Button, { size: "lg", className: "w-full", onClick: handleContinueLearning, children: ["Continue Learning", enrollment.progress > 0 && " (".concat(Math.round(enrollment.progress), "% complete)")] })) : ((0, jsx_runtime_1.jsx)(button_1.Button, { size: "lg", className: "w-full", onClick: handleEnrollment, disabled: isEnrolling, children: isEnrolling ? "Enrolling..." : "Start Learning" })) })] })] })); }; exports.default = CourseDetailPage;